/***************************************************************************
 *   Copyright (C) 2007 by Grzegorz Latarowski, Maciej Gajewski            *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/


// Qt
#include <QTcpSocket>
#include <QTcpServer>

// libcamera 
#include "camera.h"
#include "analyzer.h"    // for pimage_t 

// local
#include "autoserver.h"
#include "gexception.h"
#include "autopackets.h"



// --------------------------- constructor -----------------------------------
GAutoServer::GAutoServer( QObject* pParent /* =NULL*/ ) 
	: QObject( pParent ), _server( this )
{
	_pSocket = NULL;
	_timeout = 10000; // 1 sec
	
	camera_init();
}
// ---------------------------------------------------------------------------


// --------------------------- destructor ------------------------------------
GAutoServer::~GAutoServer()
{
	// nothing
}
// ---------------------------------------------------------------------------


// --------------------------- setSocket -------------------------------------
void GAutoServer::setSocket( QTcpSocket* pSocket )
{
	disconnectFromHost();
	_pSocket = pSocket;
}
// ---------------------------------------------------------------------------


// --------------------------- listen ----------------------------------------
/// Listens synchronously for connection on specified port/address.
/// When connection arrives, it is accepted and used for further communication
void GAutoServer::listen( quint16 port, const QHostAddress & address /*= QHostAddress::Any*/ )
{
	// disconnect current connection
	disconnectFromHost();

	// listen
	if ( ! _server.listen( address, port ) )
	{
		throw GSysError( _server.errorString() );
	}

	// wait for new connection
	if ( ! _server.waitForNewConnection(-1) )
	{
		throw GSysError( _server.errorString() );
	}
	
	// accept connection
	_pSocket = 	_server.nextPendingConnection();
	if ( ! _pSocket )
	{
		throw GSysError( tr("No incoming conection" ) );
	}

}
// ---------------------------------------------------------------------------


// --------------------------- isConnected -----------------------------------
bool GAutoServer::isConnected() const
{
	/*
	if ( _pSocket )
	{
		//return  _pSocket->state() == QAbstractSocket::ConnectedState;
		QAbstractSocket::SocketState state = _pSocket->state();
		return state == QAbstractSocket::ConnectedState;
	}
	*/
	return _pSocket != NULL;
}
// ---------------------------------------------------------------------------


// --------------------------- disconnectFromHost ----------------------------
void GAutoServer::disconnectFromHost()
{
	if ( _pSocket )
	{
		
		_pSocket->disconnectFromHost();
		if ( ! _pSocket->waitForDisconnected( _timeout ) )
		{
			throw GTimeoutError( _pSocket->errorString() );
		}
	}
}
// ---------------------------------------------------------------------------


// --------------------------- processPackets --------------------------------
/// Starts packet processing, does not return.
/// Throws configuration exception if CUD is not set and network connection is not
/// Established. Throws other exceptions on communication errors
void GAutoServer::processPackets()
{
	// check config
	if ( ! isConnected() )
	{
		throw GConfigurationError( tr("No CUD set or no connection established") );
	}

	// process packets here
	forever
	{
		GAutoPacket * pPacket = waitForPacket();
		
		if ( pPacket )
		{
            processPacket( pPacket );
            delete pPacket;
		}
		else
		{
			break;
		}
	}
	
}
// ---------------------------------------------------------------------------


// --------------------------- waitForPacket ---------------------------------
/// Waits for incomng packet
GAutoPacket* GAutoServer::waitForPacket()
{
	if ( isConnected() )
	{
		GHeader		header;

		// read header
		header.readFromSocket( _pSocket, -1 );
		// create apropriate packet
		GAutoPacket * pPacket = GAutoPacket::createPacket( header );
		// read packet
		if ( pPacket )
		{
			pPacket->readFromSocket( _pSocket, _timeout );
			
			return pPacket;
		}
		
	}

	return NULL;
}
// ---------------------------------------------------------------------------


// --------------------------- sendPacket ------------------------------------
/// Sends packet
void GAutoServer::sendPacket( const GAutoPacket & packet )
{
	if ( isConnected() )
	{
		GHeader header = packet.header();

		header.writeToSocket( _pSocket, _timeout );
		packet.writeToSocket( _pSocket, _timeout );
		
	}
}
// ---------------------------------------------------------------------------


// --------------------------- processPacket ---------------------------------
/// Prcesses incoming packet
void GAutoServer::processPacket( GAutoPacket * pPacket )
{
	if ( pPacket )
	{
		switch ( pPacket->packetType() )
		{
			// incoming params
			case GAutoPacket::PACKET_PARAMS:
				processParams( qobject_cast< GAutoParams * >( pPacket ) );
				break;
				
			case GAutoPacket::PACKET_MARKERS:
				processMarkers( qobject_cast< GAutoMarkers * >( pPacket ) );
				break;
				
			case GAutoPacket::PACKET_ROI:
				processRoi( qobject_cast< GAutoRoi * >( pPacket ) );
				break;

			// xml config file
			case GAutoPacket::PACKET_XML_CONFIG:
				processXmlConfig( qobject_cast< GAutoXmlConfig * >( pPacket ) );
				break;

			// request
			case GAutoPacket::PACKET_REQUEST:
				processRequest( qobject_cast< GAutoRequest * >( pPacket ) );
				break;

			// ignore others
			default:
				; // nothing
		}
	} // pPacket
}
// ---------------------------------------------------------------------------


// --------------------------- processRequest --------------------------------
/// Process request packet, sneding back appripriate response
void GAutoServer::processRequest( GAutoRequest * pRequest )
{
	if ( ! pRequest )
	{
		return; // TODO maybe throw here
	}
	GAutoReply * pReply = NULL;
	try
	{

		// handle various requests
		switch( pRequest->requestType() )
		{
			case GAutoRequest::REQUEST_FRAME:
			{
				// send packet
				GAutoReplyFrame * pReplyFrame = new GAutoReplyFrame( pRequest->requestId() );
				pReply = pReplyFrame;

				if ( pReplyFrame )
				{
					processRequestFrame( pReplyFrame );
				}
				else
				{
					throw GSysError( tr("Can not allocate packet" ) );
				}

			}
			break;
			
			
			case GAutoRequest::PACKET_REPLY_XML_CONFIG:
			{
				// create and send reply packet 
				GAutoReplyXmlConfig * pReplyXmlConfig = new GAutoReplyXmlConfig( pRequest->requestId() );
				pReply = pReplyXmlConfig;
				
				if ( pReplyXmlConfig )
				{
					processRequestXmlConfig( pReplyXmlConfig );
				}
				else
				{
					throw GSysError( tr("Can not allocate packet" ) );
				}
			}
			break;
			
			
			default:
				; // ignore
		}
	}
	catch( const GException& e )
	{
		// TODO handle error
	}

	// send reply
	if ( pReply )
	{
		sendPacket( *pReply );
        delete pReply;
	}

}
// ---------------------------------------------------------------------------


// ------------------------- processRequestFrame -----------------------------
void GAutoServer::processRequestFrame( GAutoReplyFrame * pReplyFrame )
{
	int width =  camera_image_width();
	int height = camera_image_height();
	
	QByteArray image( width * height, 0 );
	
	// get image from library
	uint64_t timestamp = 0;
	int state = camera_get_image( (uint8_t*)image.data(), image.size(), &timestamp );
	
	if ( state != CAMERA_OK )
	{	
		// return empty packet 
		pReplyFrame->setWidth( 0 );
		pReplyFrame->setHeight( 0 );
		pReplyFrame->setImage( QByteArray() );
		pReplyFrame->setTimestamp( 0 );
		
		return;
	}
	
	pReplyFrame->setWidth( width );
	pReplyFrame->setHeight( height );
	pReplyFrame->setImage( image );
	pReplyFrame->setTimestamp( timestamp );
}
// ---------------------------------------------------------------------------


// ------------------ GAutoServer::processXmlConfig() ------------------------
void GAutoServer::processXmlConfig( GAutoXmlConfig * pXmlConfig )
{
		// save new xml config file 
	QFile xmlConfig( XML_CONFIG_FILE );
	
	if( xmlConfig.open( QIODevice::WriteOnly | QIODevice::Text) )
	{
		xmlConfig.write( pXmlConfig->data() );
	}
}
// ---------------------------------------------------------------------------


// --------------- GAutoServer::processRequestXmlConfig() --------------------
void GAutoServer::processRequestXmlConfig( GAutoReplyXmlConfig * pReplyXmlConfig )
{
		// reply xml config file 
	QFile xmlConfig( XML_CONFIG_FILE );
	
	if( xmlConfig.open( QIODevice::ReadOnly | QIODevice::Text) )
	{
		pReplyXmlConfig->setData( xmlConfig.readAll() );
	}
}
// ---------------------------------------------------------------------------
	
	
// ------------------ GAutoServer::processMarkers() --------------------------
void GAutoServer::processMarkers( GAutoMarkers * pMarkers )
{
		// save new markers image file 
	FILE * pMarkersFile = fopen( MARKERS_FILE, "w" );
	
	if( pMarkersFile )
	{
		save_image( pMarkersFile, pMarkers->markers() );
	}
	
	fclose( pMarkersFile );
}
// ---------------------------------------------------------------------------
	
	
// ------------------ GAutoServer::processRoi() ------------------------------
void GAutoServer::processRoi( GAutoRoi * pRoi )
{
		// save new roi image file 
	FILE * pRoiFile = fopen( ROI_FILE, "w" );
	
	if( pRoiFile )
	{
		save_image( pRoiFile, pRoi->roi() );
	}
	
	fclose( pRoiFile );
}
// ---------------------------------------------------------------------------
	
	
// -------------------- GAutoServer::processParams() -------------------------
void GAutoServer::processParams( GAutoParams * pParams )
{
	qDebug( "processing params %d", pParams->params().size() );
	
	QFile paramsFile( PARAMS_FILE );
	if ( !paramsFile.open( QIODevice::WriteOnly ) ) return;
	
	QDataStream out( & paramsFile );
	out << pParams->params();
}
// ---------------------------------------------------------------------------
	


// eof
